package com.ydlclass.spi;

import com.ydlclass.config.ObjectWrapper;
import com.ydlclass.config.SpiResolver;
import com.ydlclass.exceptions.SpiException;
import com.ydlclass.loadbalancer.LoadBalance;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 实现一个简易版本的spi
 * @Author duWenJian
 * @Date 2024/3/4 13:12
 * @Version 1.0
 */
@Slf4j
public class SpiHandler {

    // 定义一个basePath指明要加载的路径
    public static final String BASE_PATH = "META-INF/services";

    // 从spi文件内容中加载到缓存中
    private static final Map<String, List<String>> SPI_CONTENT = new ConcurrentHashMap<>(8);
    //缓存的是每一个接口所对应的实现的实例
    public static final Map<Class<?>,List<ObjectWrapper<?>>> SPI_IMPLEMENT = new ConcurrentHashMap<>(32);

    // 类初始化结束之后，优先将spi的配置(内容)加载到缓存中
    static {
        // todo 怎么加载当前工程和jar包下的classPath中的资源
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        URL fileUrl = classLoader.getResource(BASE_PATH);
        if (fileUrl != null){
        File file = new File(fileUrl.getPath());
        File[] children = file.listFiles();
        if (children != null || children.length > 0) {
        for (File child : children) {
            String key = child.getName();
            List<String> value = getImplNames(child);
            SPI_CONTENT.put(key, value);
            }
        }
        }
    }

    /**
     * 根据spi的class获取spi的实现类
     * @param clazz 传过来的接口
     * @return 返回一个具体的实现类
     */
    public static <T> ObjectWrapper<T> get(Class<?> clazz) {

        // 1、优先走缓存
        List<ObjectWrapper<?>> impls = SPI_IMPLEMENT.get(clazz);
        if (impls != null && impls.size() > 0) {
            return (ObjectWrapper<T>) impls.get(0);
        }
        // 2、构建缓存
        buildCache(clazz);

        List<ObjectWrapper<?>> results = SPI_IMPLEMENT.get(clazz);
        if (results == null && results.size() == 0) {
            return null;
        }
        // 3、返回缓存
        return (ObjectWrapper<T>) results.get(0);
    }

    /**
     * 根据spi的接口名称，返回所对应的所有实现
     * @param clazz 接口限定名
     * @param <T> 泛型
     * @return 返回所有实现的List
     */
    public synchronized static <T> List<ObjectWrapper<T>> getList(Class<?> clazz) {

        // 1、优先走缓存
        List<ObjectWrapper<?>> objectWrappers = SPI_IMPLEMENT.get(clazz);
        if (objectWrappers != null && objectWrappers.size() > 0) {
            List<ObjectWrapper<T>> collect = objectWrappers.stream()
                    .map(t -> (ObjectWrapper<T>) t)
                    .collect(Collectors.toList());
            return collect;
        }
        // 2、构建缓存
        buildCache(clazz);
        // 3、构建完缓存再次获取
        objectWrappers = SPI_IMPLEMENT.get(clazz);
        if (objectWrappers != null && objectWrappers.size() > 0) {
            List<ObjectWrapper<T>> collect = objectWrappers.stream()
                    .map(t -> (ObjectWrapper<T>) t)
                    .collect(Collectors.toList());
            return collect;
        }
        return null;
    }

    /**
     *
     * @param clazz
     * @return
     */
    private static void buildCache(Class<?> clazz) {
        // 1、获取spi的实现名称
        List<String> implNames = SPI_CONTENT.get(clazz.getName());

        if (implNames == null || implNames.size() == 0){
            return;
        }

        // 2、实例化所有的实现并缓存
        List<ObjectWrapper<?>> impls = new ArrayList<>();
        for (String implName : implNames) {
            try {
                // 对spi里面的内容进行解析
                String[] codeAndTypeAndName = implName.split("-");
                if (codeAndTypeAndName.length != 3){
                    throw new SpiException("spi文件内容格式错误");
                }
                Byte code = Byte.valueOf(codeAndTypeAndName[0]);
                String type = codeAndTypeAndName[1];
                String Name = codeAndTypeAndName[2];
                Class<?> aClass = Class.forName(Name);
                Object impl = aClass.getConstructor().newInstance();
                ObjectWrapper<?> wrapper = new ObjectWrapper(code,type,impl);
                impls.add(wrapper);

            } catch (ClassNotFoundException | InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) {
                log.error("实例化:{}的实现发生异常:{}",implName,e);
            }
        }
        // 3、把实现放入缓存
        SPI_IMPLEMENT.put(clazz, impls);
    }


    /**
     * 获取文件的所有实现名称
     * @param child 文件
     * @return 返回文件的所有实现名称
     */
    private static List<String> getImplNames(File child) {

        try (
                FileReader reader = new FileReader(child);
                BufferedReader bufferedReader = new BufferedReader(reader);)
        {
            List<String> implNames = new ArrayList<>();
            while (true){
                String line = bufferedReader.readLine();
                if (line == null || "".equals(line)) {
                    break;
                }
                implNames.add(line);
            }
            return implNames;
        } catch (IOException e) {
            log.error("读取spi配置文件失败{}", e);
        }
        return null;
    }

    public static void main(String[] args) {
        SpiHandler spiHandler = new SpiHandler();
    }
}
